﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AAA.DataSource;
using AAA.Meta.Quote.Data;
using AAA.DesignPattern.Observer;
using AAA.Meta.Quote;

namespace AAA.TradeLanguage
{
    public class CurrentTime : IObserver
    {
        private IDataSource _dataSource;
        private DateTime _dtCurrentTime;
        private Dictionary<string, int> _dicDataIndex;
        private DateTime _dtSessionStart;
        private DateTime _dtSessionEnd;
        private int _iTimeInterval = 1;
        private DateTime _dtDataStartTime;
        private DateTime _dtDataEndTime;
        private string _strBaseSymbolId;

        public string BaseSymbolId
        {
            get { return _strBaseSymbolId; }
            set { _strBaseSymbolId = value; }
        }

        public DateTime SessionStartTime
        {
            get { return _dtSessionStart; }
            set { _dtSessionStart = value; }
        }

        public DateTime SessionEndTime
        {
            get { return _dtSessionEnd; }
            set { _dtSessionEnd = value; }
        }

        public int TimeInterval
        {
            get { return _iTimeInterval; }
            set { _iTimeInterval = value; }
        }

        public IDataSource DataSource
        {
            get { return _dataSource; }
            set 
            {                
                _dtDataEndTime = DateTime.MinValue;
                _dtDataStartTime = DateTime.MaxValue;                
                _dataSource = value;
                _dataSource.Attach(this);
                _dicDataIndex = new Dictionary<string, int>();
                RecalculateDataTime();
                _dtCurrentTime = _dtDataStartTime;
            }
        }

        private void RecalculateDataTime()
        {
            List<BarRecord> lstBarData;
            foreach (string strSymbolId in _dataSource.GetSymbolList())
            {
                lstBarData = _dataSource.GetBar(strSymbolId);
                if (lstBarData.Count == 0)
                    continue;

                if (_dtDataStartTime.CompareTo(lstBarData[0].BarDateTime) > 0)
                    _dtDataStartTime = lstBarData[0].BarDateTime;

                if (_dtDataEndTime.CompareTo(lstBarData[lstBarData.Count - 1].BarDateTime) < 0)
                    _dtDataEndTime = lstBarData[lstBarData.Count - 1].BarDateTime;

                if(_dicDataIndex.ContainsKey(strSymbolId) == false)
                    _dicDataIndex.Add(strSymbolId, 0);
            }

            if (_dtCurrentTime == DateTime.MaxValue)
                _dtCurrentTime = _dtDataStartTime;
        }

        private int FindDataIndex(DateTime dtCurrentTime, List<BarRecord> lstBarData, int iIndex)
        {
            int iCurrentIndex = iIndex;

            if (lstBarData == null)
                return 0;

            if (iCurrentIndex < 0)
                return 0;

            if (lstBarData.Count == 0)
                return 0;

            if(iCurrentIndex > 0)
                if ((lstBarData[iCurrentIndex].BarDateTime.CompareTo(dtCurrentTime) > 0) && ((lstBarData[iCurrentIndex - 1].BarDateTime.CompareTo(dtCurrentTime) <= 0)))
                    return iIndex;

            if (lstBarData[iCurrentIndex].BarDateTime.CompareTo(_dtCurrentTime) < 0)
            {
                while ((lstBarData[iCurrentIndex].BarDateTime.CompareTo(_dtCurrentTime) < 0) && (iCurrentIndex + 1 < lstBarData.Count))
                    iCurrentIndex++;
            }
            else
            {
                while ((lstBarData[iCurrentIndex].BarDateTime.CompareTo(_dtCurrentTime) > 0) && (iCurrentIndex > 0))
                    iCurrentIndex--;
                if (iCurrentIndex + 1 < lstBarData.Count && (lstBarData[iCurrentIndex].BarDateTime.CompareTo(_dtCurrentTime) < 0))
                    iCurrentIndex++;
            }
            return iCurrentIndex;
        }


        //計算給定時間的BarTime (BarTime為讓時段的結束時間)
        public DateTime CurrentBarTime(DateTime dtCurrentTime, BarCompression barCompression)
        {
            DateTime currentBarTime = new DateTime(dtCurrentTime.Year,
                                                   dtCurrentTime.Month,
                                                   dtCurrentTime.Day,
                                                   SessionStartTime.Hour,
                                                   SessionStartTime.Minute,
                                                   dtCurrentTime.Second);
            long lTickDiff;
            int iAddInterval;

            switch (barCompression.DataCompression)
            {
                case BarCompressionEnum.Tick:
                    break;
                case BarCompressionEnum.Min:
                    if (currentBarTime.CompareTo(dtCurrentTime) == 0)
                    {
                        currentBarTime = currentBarTime.AddMinutes(barCompression.Interval);
                    }
                    else
                    {
                        lTickDiff = dtCurrentTime.Ticks - currentBarTime.Ticks;
                        iAddInterval = (int)Math.Floor((lTickDiff / TimeSpan.TicksPerSecond - 1) / (barCompression.Interval * 60.0)) + 1;
                        currentBarTime = currentBarTime.AddMinutes(barCompression.Interval * iAddInterval);
                    }
/*
                    while (currentBarTime.CompareTo(dtCurrentTime) < 0)
                        currentBarTime = currentBarTime.AddMinutes(barCompression.Interval);
 */ 
                    break;
                case BarCompressionEnum.Hour:
                    while (currentBarTime.CompareTo(dtCurrentTime) < 0)
                        currentBarTime = currentBarTime.AddHours(barCompression.Interval);
                    break;
                case BarCompressionEnum.Daily:
                    while (currentBarTime.CompareTo(dtCurrentTime) < 0)
                        currentBarTime = currentBarTime.AddDays(barCompression.Interval);
                    break;
            }

            return currentBarTime;
        }
       
        public DateTime CurrentDateTime
        {
            get
            {
                //return _dtCurrentTime == DateTime.MinValue ? _dtDataStartTime : _dtCurrentTime;
                return _dtCurrentTime;
            }
            set
            {
                _dtCurrentTime = value;
            }
        }

        //重新計算每個Symbol目前相對應的DataIndex
        private void RecalculateDataIndex()
        {
            int iCurrentIndex;
            List<BarRecord> lstBarData;
            foreach (string strSymbolId in DataSource.GetSymbolList())
            {
                if (_dicDataIndex.ContainsKey(strSymbolId))
                {
                    iCurrentIndex = _dicDataIndex[strSymbolId];
                }
                else
                {
                    iCurrentIndex = 0;
                    _dicDataIndex.Add(strSymbolId, iCurrentIndex);
                }

                lstBarData = DataSource.GetBar(strSymbolId);
                iCurrentIndex = FindDataIndex(_dtCurrentTime, lstBarData, iCurrentIndex);

                _dicDataIndex[strSymbolId] = iCurrentIndex;
            }

        }

        public DateTime BarStartTime(DateTime dtCurrentBarTime, BarCompression barCompression)
        {
            DateTime dtReturnTime = dtCurrentBarTime;

            switch (barCompression.DataCompression)
            {
                case BarCompressionEnum.Min:
                    dtReturnTime = dtCurrentBarTime.AddMinutes(-1 * barCompression.Interval);
                    break;
                case BarCompressionEnum.Hour:
                    dtReturnTime = dtCurrentBarTime.AddHours(-1 * barCompression.Interval);
                    break;
                case BarCompressionEnum.Daily:
                    dtReturnTime = new DateTime(dtCurrentBarTime.Year, dtCurrentBarTime.Month, dtCurrentBarTime.Day,
                                                SessionStartTime.Hour, SessionStartTime.Minute, SessionStartTime.Second);
                    break;
            }

            return dtReturnTime;
        }

        public DateTime BarEndTime(DateTime dtCurrentBarTime, BarCompression barCompression)
        {
            return dtCurrentBarTime.AddSeconds(-1);
        }

        //調整時間, 當時間為SessionEnd時, 將時間移至下一天的開盤時間
        private DateTime AdjustTime(DateTime dtCurrent)
        {
            DateTime dtReturn = new DateTime(dtCurrent.Ticks);
            DateTime dtSessionEnd = new DateTime(dtCurrent.Year, dtCurrent.Month, dtCurrent.Day,
                                                 SessionEndTime.Hour, SessionEndTime.Minute, SessionEndTime.Second);

            if(dtReturn.CompareTo(dtSessionEnd) > 0)
            {
                dtSessionEnd = dtSessionEnd.AddDays(1);
                dtReturn = new DateTime(dtSessionEnd.Year, dtSessionEnd.Month, dtSessionEnd.Day,
                                        SessionStartTime.Hour, SessionStartTime.Minute, SessionStartTime.Second);
                //dtReturn.AddSeconds(TimeInterval);
            }

            return dtReturn;
        }

        public void MovePrevious()
        {
            CurrentDateTime = AdjustTime(CurrentDateTime.AddSeconds(TimeInterval * -1));
            RecalculateDataIndex();
        }

        public void MoveNext()
        {
            CurrentDateTime = AdjustTime(CurrentDateTime.AddSeconds(TimeInterval));
            RecalculateDataIndex();
        }

        public void RemoveSymbol(string strSymbolId)
        {
            if (_dicDataIndex.ContainsKey(strSymbolId))
                _dicDataIndex.Remove(strSymbolId);
            DataSource.RemoveSymbol(strSymbolId);
        }

        public DateTime DataStartTime
        {
            get
            {
                return _dtDataStartTime;
            }
        }

        public DateTime DataEndTime
        {
            get
            {
                return _dtDataEndTime;
            }
        }

        public int GetDataIndex(string strSymbolId)
        {
            if (_dicDataIndex == null)
                return int.MinValue;
            if (_dicDataIndex.ContainsKey(strSymbolId) == false)
                return int.MinValue;

            return _dicDataIndex[strSymbolId];
        }

        public void Reset()
        {
            List<string> lstSymbolId = new List<string>();
            
            foreach (string strSymbolId in _dicDataIndex.Keys)
            {
                lstSymbolId.Add(strSymbolId);                
            }
            RecalculateDataIndex();
//            foreach(string strSymbolId in lstSymbolId)
//                _dicDataIndex[strSymbolId] = 0;
//            CurrentDateTime = _dtDataStartTime;
        }

        #region IObserver 成員

        public void Update(object oSource, IMessageInfo miMessage)
        {
            try
            {
                switch (miMessage.MessageSubject)
                {
                    case DataSourceConstants.ADD_SYMBOL:                        
                        string strSymbolId = miMessage.Message.ToString();
                        if (_dicDataIndex.ContainsKey(strSymbolId))
                            return;
                        _dicDataIndex.Add(strSymbolId, FindDataIndex(CurrentDateTime, DataSource.GetBar(strSymbolId), 0));

                        RecalculateDataTime();
                        break;

                    case DataSourceConstants.BAR_RECEIVE:
                        List<BarRecord> lstBarRecord = (List<BarRecord>)miMessage.Message;
                        if(lstBarRecord.Count == 0)
                            break;

                        if (lstBarRecord[0].SymbolId != BaseSymbolId)
                            break;

                        while (CurrentDateTime.CompareTo(lstBarRecord[lstBarRecord.Count - 1].BarDateTime) < 0)
                        {
                            CurrentDateTime = AdjustTime(CurrentDateTime.AddSeconds(TimeInterval));
                        }

                        RecalculateDataIndex();
                        break;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #endregion
    }
}
